package com.hup.joystick.model;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.hup.joystick.constant.DeviceKey.StandardKeys;
import com.hup.joystick.constant.FunctionConstant.FunctionType;
import com.hup.joystick.constant.FunctionConstant.MouseButton;
import com.hup.joystick.constant.FunctionConstant.MouseMoveType;
import com.hup.joystick.service.joyEvent.IJoyEventHandler;
import com.hup.joystick.view.functionSetting.FunctionSettingItem;
import com.hup.utils.commons.TextUtil;
import com.hup.utils.commons.exception.AppExceptionHandler;
import com.hup.utils.commons.io.BaseConfig;
import com.hup.utils.commons.io.FileUtil;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Objects;

/**
 * 功能配置类
 *
 * @author hugan
 * @date 2020/10/23
 */
@Setter
@Getter
@Log4j2
public class FunctionConfig implements BaseConfig<FunctionConfig> {
    private static final String BUILD_IN_FUNCTION = "/function/desktop.function";
    private static final FunctionItemComparator FUNCTION_ITEM_COMPARATOR = new FunctionItemComparator();

    public static final String EXT = ".function";

    @JSONField(serialize = false)
    public boolean enable = true;

    /**
     * 仅在主界面业务逻辑中使用
     */
    @JSONField(serialize = false)
    public boolean foundInRecord = false;

    /**
     * 独占模式:true-独占
     */
    public boolean exclusive;
    /**
     * 指定的生效程序
     */
    public ArrayList<String> lstApp = new ArrayList<>();

    /**
     * 具体的功能
     */
    public ArrayList<FunctionItem> lstFunction = new ArrayList<>();

    /**
     * 配置名,配置文件名(不带扩展名)
     */
    @JSONField(serialize = false)
    public String name;

    @Override
    public String configPath() {
        return Path.getBaseDir() + FileUtil.getSafeFileName(name) + EXT;
    }

    public void viewToFunction(FunctionSettingItem view) {
        FunctionType type = view.getType();
        FunctionItem function = new FunctionItem();
        function.functionType = type;
        if (type == FunctionType.NONE) {
            return;
        } else if (type == FunctionType.QUICK_MENU) {
            function.lstStandardKey = view.getStandardKeys();
        } else if (type == FunctionType.MOUSE_MOVE) {
            function.mouseMoveType = view.cbMouseMoveType.getSelectedItem();
        } else if (type == FunctionType.MOUSE_BUTTON) {
            function.mouseButton = view.cbMouseButton.getSelectedItem();
            function.lstStandardKey = view.getStandardKeys();
        } else if (type == FunctionType.KEYBOARD) {
            function.lstKeyCode = view.getKeyboardCodes();
            function.lstStandardKey = view.getStandardKeys();
        }
        function.remark = view.getRemark();
        lstFunction.add(function);
    }

    public void sortConfigFields() {
        lstApp.sort(String::compareTo);
        lstFunction.sort(FUNCTION_ITEM_COMPARATOR);
    }

    @Override
    public String toString() {
        if (lstFunction == null) {
            return "FunctionConfig{" + name + "}";
        }
        return TextUtil.getText(lstFunction, "\n", "FunctionConfig{" + name + "\n[", "]}", (sb, hasNext, next) -> next.getString(sb));
    }

    public static FunctionConfig getBuildInFunction() {
        log.debug("获取内置配置={}", BUILD_IN_FUNCTION);
        FunctionConfig config = null;
        try (InputStream ips = FunctionConfig.class.getResourceAsStream(BUILD_IN_FUNCTION)) {
            if (ips != null) config = JSON.parseObject(ips, StandardCharsets.UTF_8, FunctionConfig.class);
        } catch (IOException e) {
            log.warn("内置配置异常?={}", AppExceptionHandler.handle(e));
        }
        if (config == null) config = new FunctionConfig();
        config.name = "内置配置";
        return config;
    }

    /**
     * 获取用户配置的功能
     * 无配置文件时,添加[内置配置]
     *
     * @return 长度至少是1
     */
    public static ArrayList<FunctionConfig> getUserFunctions() {
        ArrayList<FunctionConfig> lstFunction = new ArrayList<>();
        File configDir = new File(Path.getBaseDir());
        File[] files = configDir.listFiles((dir, name) -> name.endsWith(EXT));
        if (files != null) {
            for (File file : files) {
                FunctionConfig function = new FunctionConfig();
                String fileName = file.getName();
                String configName = fileName.substring(0, fileName.length() - EXT.length());
                function.name = configName;
                lstFunction.add(function.loadFromFile());
                function.name = configName;//loadFromFile后需要重新赋值
            }
        }
        if (lstFunction.isEmpty()) lstFunction.add(getBuildInFunction());
        return lstFunction;
    }

    /**
     * 合并配置
     *
     * @param lstConfig 都是[非禁用]的配置
     */
    public static void mixConfig(FunctionConfig receiver, ArrayList<FunctionConfig> lstConfig) {
        int size = lstConfig.size();
        if (size == 0) return;
        FunctionConfig config = lstConfig.get(0);
        receiver.lstFunction.clear();
        receiver.lstFunction.addAll(config.lstFunction);//首个配置中,也可能存在按键冲突,但这明显是用户所为,所以做不特殊合并过滤
        if (size == 1) return;//只有一个配置,不用合并

        //合并配置
        for (int i = 1; i < size; i++) {
            FunctionConfig toMixConfig = lstConfig.get(i);
            for (FunctionItem toMixFunction : toMixConfig.lstFunction) {
                if (canMix(toMixFunction, receiver.lstFunction)) receiver.lstFunction.add(toMixFunction);
            }
        }
    }

    /**
     * @param toMixFunction 被判断能否合并到已有列表中的功能
     * @param oldFunctions 已有的功能列表
     */
    private static boolean canMix(FunctionItem toMixFunction, ArrayList<FunctionItem> oldFunctions) {
        if (toMixFunction.functionType == FunctionType.MOUSE_MOVE) {
            //鼠标移动,一个功能要占用多个按键的
            for (FunctionItem old : oldFunctions) {
                if (old.functionType == FunctionType.MOUSE_MOVE) return false;//已有鼠标移动功能:不合并
                for (String key : old.lstStandardKey) {
                    if (StandardKeys.isArrow(key) || StandardKeys.isStick(key)) return false;//已有箭头/摇杆按键:不合并
                }
            }
            return true;
        } else if (toMixFunction.functionType == FunctionType.MOUSE_BUTTON) {
            //鼠标按键:已有功能按键,有包含当前按键的,不合并
            for (FunctionItem old : oldFunctions) {
                if (old.functionType == FunctionType.MOUSE_MOVE) continue;//在下面的for中判断
                if (old.lstStandardKey.size() < toMixFunction.lstStandardKey.size()) continue;//可能可以合并,在下面的for中判断
                boolean oldContainAll = true;
                for (String key : toMixFunction.lstStandardKey) {
                    if (!old.lstStandardKey.contains(key)) {
                        oldContainAll = false;
                        break;
                    }
                }
                if (oldContainAll) return false;
            }
        }
        //快捷菜单,键盘按键
        for (FunctionItem old : oldFunctions) {
            if (old.functionType == FunctionType.MOUSE_MOVE) {
                //已有鼠标移动功能:如果与当前function的按键有重复,不合并
                switch (old.mouseMoveType) {
                    case ARROW:
                        for (String key : toMixFunction.lstStandardKey) {
                            if (StandardKeys.isArrow(key)) return false;
                        }
                        break;
                    case STICK_LEFT:
                        for (String key : toMixFunction.lstStandardKey) {
                            if (key.startsWith("摇杆L")) return false;
                        }
                        break;
                    case STICK_RIGHT:
                        for (String key : toMixFunction.lstStandardKey) {
                            if (key.startsWith("摇杆R")) return false;
                        }
                        break;
                }
            } else {
                //标准按键完全一样的,不合并(比toMixFunction.functionType == FunctionType.MOUSE_BUTTON时要求更宽松些)
                if (old.lstStandardKey.size() != toMixFunction.lstStandardKey.size()) continue;
                //lstStandardKey是无重复,但无序的(如AY,YA是相同按键),用户配置时的键入顺序不一样,所以不能直接用list.equal
                boolean isSame = true;
                for (String oldKey : old.lstStandardKey) {
                    if (!toMixFunction.lstStandardKey.contains(oldKey)) {
                        isSame = false;
                        break;
                    }
                }
                if (isSame) return false;
            }
        }
        return true;
    }

    public static ArrayList<FunctionConfig> getMatchConfig(String frontApp, ArrayList<FunctionConfig> lstConfig) {
        ArrayList<FunctionConfig> match = new ArrayList<>();
        for (FunctionConfig config : lstConfig) {
            if (!config.enable) continue;
            if (config.lstApp.isEmpty()) {
                match.add(config);//无[生效程序]
                continue;
            }
            //有[生效程序]
            for (String app : config.lstApp) {
                if (!Objects.equals(app, frontApp)) continue;
                //符合[生效程序]
                if (config.exclusive) {
                    match.clear();
                    match.add(config);
                    //log.info("独占={}", config.name);
                    return match;//独占
                } else {
                    match.add(config);
                    break;
                }
            }
        }
        return match;
    }

    /**
     * 功能类
     */
    @Data
    public static class FunctionItem {
        private static final char TXT_FUNCTION_SPLITER = ':';

        /**
         * 功能类别: 除{@link FunctionType#NONE}外的类型
         */
        public FunctionType functionType;

        /**
         * {@link FunctionType#MOUSE_MOVE};
         * {@link MouseMoveType}
         */
        public MouseMoveType mouseMoveType;
        /**
         * {@link FunctionType#MOUSE_BUTTON}
         */
        public MouseButton mouseButton;

        /**
         * {@link FunctionType#KEYBOARD} 时的键盘按键 {@link KeyEvent#getKeyCode()}
         * 符合类型时,但用户无配置时,对象不为空,长度为0
         */
        public ArrayList<Integer> lstKeyCode;

        /**
         * 无重复的标准按键,用于:{@link FunctionType#QUICK_MENU},{@link FunctionType#MOUSE_BUTTON},{@link FunctionType#KEYBOARD}
         * 符合类型时,但用户无配置时,对象不为空,长度为0
         */
        public ArrayList<String> lstStandardKey;

        /**
         * 功能备注
         */
        public String remark;

        public String getString(StringBuilder sb) {
            boolean needReturn = false;
            if (sb == null) {
                sb = new StringBuilder();
                needReturn = true;
            }
            if (remark != null) {
                sb.append(remark).append(TXT_FUNCTION_SPLITER);
            }
            switch (functionType) {
                case QUICK_MENU:
                    sb.append(functionType.desc);
                    sb.append(TXT_FUNCTION_SPLITER);
                    sb.append(TextUtil.getText(lstStandardKey, "+"));
                    break;
                case MOUSE_MOVE:
                    sb.append(functionType.desc);
                    sb.append(TXT_FUNCTION_SPLITER);
                    sb.append(mouseMoveType.desc);
                    break;
                case MOUSE_BUTTON:
                    sb.append("鼠标-");
                    sb.append(mouseButton.desc);
                    sb.append(TXT_FUNCTION_SPLITER);
                    sb.append(TextUtil.getText(lstStandardKey, "+"));
                    break;
                case KEYBOARD:
                    if (!lstKeyCode.isEmpty()) {
                        sb.append(TextUtil.getText(lstKeyCode, (sb1, hasNext, next) -> {
                            sb1.append(KeyEvent.getKeyText(next));
                            if (hasNext) sb1.append('+');
                        }));
                    } else {
                        sb.append("未设置按键");
                    }
                    sb.append(TXT_FUNCTION_SPLITER);
                    sb.append(TextUtil.getText(lstStandardKey, "+"));
                    break;
            }
            return needReturn ? sb.toString() : null;
        }
    }

    /**
     * {@link FunctionItem} 排序类
     * 排序规则:
     * .按类型排序
     * .键盘按键时,按标准键数量排序,
     * 此处影响 {@link IJoyEventHandler}的多按键处理逻辑(实际使用时,应该很少有多标准按键的情况)
     */
    private static class FunctionItemComparator implements Comparator<FunctionItem> {
        @Override
        public int compare(FunctionItem o1, FunctionItem o2) {
            //.按类型排序
            int res = o1.functionType.compareTo(o2.functionType);
            if (res != 0) return res;
            if (o1.functionType == FunctionType.MOUSE_BUTTON) return o1.mouseButton.compareTo(o2.mouseButton);
            if (o1.functionType == FunctionType.MOUSE_MOVE) return o1.mouseMoveType.compareTo(o2.mouseMoveType);
            if (o1.functionType == FunctionType.KEYBOARD) {
                int size1 = o1.lstStandardKey == null ? 0 : o1.lstStandardKey.size();
                int size2 = o2.lstStandardKey == null ? 0 : o2.lstStandardKey.size();
                return size1 - size2;
            }
            return 0;
        }
    }

}
